package generateur;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.JOptionPane;
import javax.swing.SwingWorker;

import logique.parser.Parser;
import logique.parser.factories.ATTFactory;
import logique.parser.factories.ATVFactory;
import logique.parser.factories.ClassementFactory;
import logique.parser.factories.TailleVilleFactory;
import logique.reseau.atouts.AtoutTouristiqueTroncon;
import logique.reseau.atouts.AtoutTouristiqueVille;
import logique.reseau.classements.Classement;
import logique.reseau.tailles.TailleVille;

import org.dom4j.io.XMLWriter;
import org.dom4j.tree.BaseElement;
import org.xml.sax.SAXException;

/**
 * 
 * @author camille
 *
 */
public class GenerateurReseauXML extends SwingWorker<Void, Void> {

	private static final String FILE_EXT = ".xml";

	private static final String VILLE_PREFIX = "ville_";
	private static final String ROUTE_PREFIX = "route_";

	private static final int LG_MAX = 1000;

	public static final String STATE_PROPERTY_NAME = "step";

	private InterfaceGRXML controller;

	private Random random;

	private int nbVilles;
	private int connexiteMin;
	private ArrayList<Integer> villesAvisiter;

	private String cheminFichier;

	public GenerateurReseauXML(InterfaceGRXML controller, int nbVilles, int connexiteMin, String cheminFichier) {

		this.controller = controller;

		this.nbVilles = nbVilles;
		this.connexiteMin = connexiteMin;
		villesAvisiter = new ArrayList<Integer>();

		this.cheminFichier = cheminFichier;

		this.random = new Random();
	}

	@Override
	public Void doInBackground() throws Exception {
		ecrireReseau();
		return null;
	}

	@Override
	protected void done() {

		setProgress(100);

		if (controller != null) {
			controller.setAllEnabled(true);
			controller.getProgressDialog().setVisible(false);
			JOptionPane.showMessageDialog(controller, "Creation terminee", "Information", JOptionPane.INFORMATION_MESSAGE);
		}
	}

	private void ecrireReseau() throws IOException, SAXException {

		firePropertyChange(STATE_PROPERTY_NAME, null, "Ecriture du fichier");
		float fait = 0;
		float aFaire = nbVilles + connexiteMin;

		String cible = cheminFichier;

		if (!cible.endsWith(FILE_EXT)) {
			cible += FILE_EXT;
		}

		XMLWriter writer = new XMLWriter(new FileWriter(cible));
		BaseElement rootElement = new BaseElement(Parser.ROOT_NODE_NAME);

		writer.startDocument();
		writer.writeOpen(rootElement);

		for (int i = 0; i < nbVilles; i++) {

			firePropertyChange(STATE_PROPERTY_NAME, null, "Ecriture d'un noeud '" + Parser.VILLE_NODE_NAME + "' (" + i + "/" + nbVilles + ")");

			fait++;
			setProgress((int) (100.0f * fait / aFaire));

			ecrireVille(writer, i);
		}

		for (int i = 0; i < connexiteMin; i++) {

			firePropertyChange(STATE_PROPERTY_NAME, null, "Ecriture d'un noeud '" + Parser.ROUTE_NODE_NAME + "' (" + i + "/" + connexiteMin + ")");

			fait++;
			setProgress((int) (100.0f * fait / aFaire));

			ecrireRoute(writer, i);
		}

		writer.writeClose(rootElement);

		rootElement.clearContent();

		writer.endDocument();

		writer.close();
	}

	private void ecrireVille(XMLWriter writer, int id) throws IOException {

		BaseElement villeElement = new BaseElement(Parser.VILLE_NODE_NAME);

		writer.writeOpen(villeElement);

		ecrireNom(writer, VILLE_PREFIX + id);
		ecrireTaille(writer, getTailleAleatoire());
		ecrireATV(writer, getATVAleatoire());

		writer.writeClose(villeElement);

		villeElement.clearContent();
	}

	private TailleVille getTailleAleatoire() {
		switch (random.nextInt(3)) {
			case 0:
				return TailleVilleFactory.getInstance().getInstanceTailleVille(TailleVilleFactory.PETITE);

			case 1:
				return TailleVilleFactory.getInstance().getInstanceTailleVille(TailleVilleFactory.MOYENNE);

			case 2:
				return TailleVilleFactory.getInstance().getInstanceTailleVille(TailleVilleFactory.GRANDE);

			default:
				return null;
		}
	}

	private AtoutTouristiqueVille getATVAleatoire() {
		if (random.nextBoolean()) {
			return ATVFactory.getInstance().getInstanceATV(ATVFactory.ATV_GENERIQUE);
		}
		else {
			return null;
		}
	}

	private void ecrireTaille(XMLWriter writer, TailleVille taille) throws IOException {
		BaseElement tailleElement = new BaseElement(Parser.TYPE_NODE_NAME);

		writer.writeOpen(tailleElement);
		writer.write(taille.toString());
		writer.writeClose(tailleElement);

		tailleElement.clearContent();
	}

	private void ecrireATV(XMLWriter writer, AtoutTouristiqueVille atout) throws IOException {
		BaseElement atoutElement = new BaseElement(Parser.TOURIST_NODE_NAME);

		writer.writeOpen(atoutElement);
		writer.write((atout != null) ? Parser.IND_POSITIVE : Parser.IND_NEGATIVE);
		writer.writeClose(atoutElement);

		atoutElement.clearContent();
	}

	private void ecrireRoute(XMLWriter writer, int id) throws IOException {
		BaseElement routeElement = new BaseElement(Parser.ROUTE_NODE_NAME);

		writer.writeOpen(routeElement);

		ecrireNom(writer, ROUTE_PREFIX + id);
		ecrireClassement(writer, getClassementAleatoire());

		resetVisites();

		int vD = getVisiteAleatoire();
		int vA;

		while (!villesAvisiter.isEmpty()) {
			vA = getVisiteAleatoire();
			ecrireTroncon(writer, vD, vA);
			vD = vA;
		}

		writer.writeClose(routeElement);

		routeElement.clearContent();
	}

	private int getVisiteAleatoire() {

		if (villesAvisiter.isEmpty()) {
			return random.nextInt(nbVilles);
		}
		else {
			return villesAvisiter.remove(random.nextInt(villesAvisiter.size()));
		}
	}

	private void resetVisites() {

		villesAvisiter.clear();

		for (int i = 0; i < nbVilles; i++) {
			villesAvisiter.add(i);
		}
	}

	private Classement getClassementAleatoire() {
		switch (random.nextInt(3)) {
			case 0:
				return ClassementFactory.getInstance().getInstanceClassement(ClassementFactory.AUTOROUTE);

			case 1:
				return ClassementFactory.getInstance().getInstanceClassement(ClassementFactory.NATIONALE);

			case 2:
				return ClassementFactory.getInstance().getInstanceClassement(ClassementFactory.DEPARTEMENTALE);

			default:
				return null;
		}
	}

	private void ecrireNom(XMLWriter writer, String nom) throws IOException {
		BaseElement nomElement = new BaseElement(Parser.NOM_NODE_NAME);

		writer.writeOpen(nomElement);
		writer.write(nom);
		writer.writeClose(nomElement);

		nomElement.clearContent();
	}

	private void ecrireClassement(XMLWriter writer, Classement classement) throws IOException {
		BaseElement classementElement = new BaseElement(Parser.TYPE_NODE_NAME);

		writer.writeOpen(classementElement);
		writer.write(classement.toString());
		writer.writeClose(classementElement);

		classementElement.clearContent();
	}

	private void ecrireTroncon(XMLWriter writer, int ville1, int ville2) throws IOException {
		BaseElement tronconElement = new BaseElement(Parser.TRONCON_NODE_NAME);

		writer.writeOpen(tronconElement);

		ecrireVille1(writer, VILLE_PREFIX + ville1);
		ecrireVille2(writer, VILLE_PREFIX + ville2);
		ecrireVitesse(writer, Integer.toString(getVitesseAleatoire()));
		ecrireATT(writer, getATTAleatoire());
		ecrireRadar(writer, random.nextBoolean());
		ecrirePeage(writer, random.nextBoolean());
		ecrireLongueur(writer, Integer.toString(random.nextInt(LG_MAX)));

		writer.writeClose(tronconElement);

		tronconElement.clearContent();
	}

	private AtoutTouristiqueTroncon getATTAleatoire() {
		if (random.nextBoolean()) {
			return ATTFactory.getInstance().getInstanceATT(ATTFactory.ATT_GENERIQUE);
		}
		else {
			return null;
		}
	}

	private int getVitesseAleatoire() {
		switch (random.nextInt(5)) {
			case 0:
				return 50;

			case 1:
				return 70;

			case 2:
				return 90;

			case 3:
				return 110;

			case 4:
				return 130;

			default:
				return 50;
		}
	}

	private void ecrireVille1(XMLWriter writer, String ville1) throws IOException {
		BaseElement villeElement = new BaseElement(Parser.VILLE1_NODE_NAME);

		writer.writeOpen(villeElement);
		writer.write(ville1);
		writer.writeClose(villeElement);

		villeElement.clearContent();
	}

	private void ecrireVille2(XMLWriter writer, String ville2) throws IOException {
		BaseElement villeElement = new BaseElement(Parser.VILLE2_NODE_NAME);

		writer.writeOpen(villeElement);
		writer.write(ville2);
		writer.writeClose(villeElement);

		villeElement.clearContent();
	}

	private void ecrireVitesse(XMLWriter writer, String vitesse) throws IOException {
		BaseElement vitesseElement = new BaseElement(Parser.VITESSE_NODE_NAME);

		writer.writeOpen(vitesseElement);
		writer.write(vitesse);
		writer.writeClose(vitesseElement);

		vitesseElement.clearContent();
	}

	private void ecrireATT(XMLWriter writer, AtoutTouristiqueTroncon atout) throws IOException {
		BaseElement atoutElement = new BaseElement(Parser.TOURIST_NODE_NAME);

		writer.writeOpen(atoutElement);
		writer.write((atout != null) ? Parser.IND_POSITIVE : Parser.IND_NEGATIVE);
		writer.writeClose(atoutElement);

		atoutElement.clearContent();
	}

	private void ecrireRadar(XMLWriter writer, boolean radar) throws IOException {
		BaseElement radarElement = new BaseElement(Parser.RADAR_NODE_NAME);

		writer.writeOpen(radarElement);
		writer.write((radar) ? Parser.IND_POSITIVE : Parser.IND_NEGATIVE);
		writer.writeClose(radarElement);

		radarElement.clearContent();
	}

	private void ecrirePeage(XMLWriter writer, boolean peage) throws IOException {
		BaseElement peageElement = new BaseElement(Parser.PAYANT_NODE_NAME);

		writer.writeOpen(peageElement);
		writer.write((peage) ? Parser.IND_POSITIVE : Parser.IND_NEGATIVE);
		writer.writeClose(peageElement);

		peageElement.clearContent();
	}

	private void ecrireLongueur(XMLWriter writer, String longueur) throws IOException {
		BaseElement longueurElement = new BaseElement(Parser.LONGUEUR_NODE_NAME);

		writer.writeOpen(longueurElement);
		writer.write(longueur);
		writer.writeClose(longueurElement);

		longueurElement.clearContent();
	}
}
